package android.svgview;

import java.util.Observable;

import android.content.Context;
import android.os.Vibrator;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.widget.ZoomControls;

public class ViewListener extends Observable implements OnTouchListener {

	private final int ZOOM_SPEED = 30;
	private final int VIBRATE_TIME = 50;
	
	private ViewState viewState;
	private ZoomControls zoomControls;
	
	private float actualX;
	private float actualY;
	private float _x;
	private float _y;
	private float viewHeight;
	private float viewWidth;
	private float dx;
	private float dy;
	private float zoom = 1;
		
	private final int touchTimeout;
	private final int scaledTouchSlop;
	private final Vibrator vibrator;
	
	private enum Mode {
		MOVE, ZOOM
	}
	
	private Mode mode = Mode.MOVE;
	
	public ViewListener(Context context) {
		touchTimeout = ViewConfiguration.getLongPressTimeout();
		scaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
		vibrator = (Vibrator)context.getSystemService("vibrator");
	}

	public void setViewState(ViewState viewState){
		this.viewState = viewState;
	}
	
	public boolean onTouch(View view, MotionEvent event) {
		final int action = event.getAction();
        final float previousX = event.getX();
        final float previousY = event.getY();
        viewHeight = viewState.getViewHeight();
		viewWidth = viewState.getViewWidth();
        
        switch (action) {
        	case MotionEvent.ACTION_DOWN:
        		view.postDelayed(longTouchRunnable, touchTimeout);
        		actualX = previousX/viewState.getZoom();
        		actualY = previousY/viewState.getZoom();
        		break;

        	case MotionEvent.ACTION_MOVE: 
    
        		final float scrollDistance;
        		//vypocte posun v obou osach s ohledem na aktualni zoom, pro posun
	        	dx = (previousX/viewState.getZoom() - actualX);
	        	dy = (previousY/viewState.getZoom() - actualY);
	        	
	        	scrollDistance = (float)Math.sqrt(dx*dx + dy*dy);
	        	
	        	//rozlisi zda slo o pohyb, tedy byla dostatecna diference nebo
	        	//zda slo jeste o dotyk, tedy diference je mala
	        	if(scrollDistance >= (scaledTouchSlop/2)){
	        		mode = Mode.MOVE;
	        		view.removeCallbacks(longTouchRunnable);
	        	}
	        	
	        	//vypocet zoomu z posunu, zatim z posunu X, nejde zmenit orientace ZoomControls na vertikalni
	        	if(mode == Mode.ZOOM){
	        		zoom += (dx)/ZOOM_SPEED;
	        		if(zoom <= 5 && zoom >= 1){
	        			viewState.setZoom(zoom);
	        		}
	        		if(zoom < 1)
	        			zoom = 1;
	        			viewState.setZoom(zoom);
	        		}
	        		if(zoom > 5){
	        			zoom = 5;
	        			viewState.setZoom(zoom);
	        		}
	        	if(mode == Mode.MOVE){
	        		//urci absolutni adresu x (pri zoom = 0) ma roh horni levy roh hodnotu _x,_y = 0,
		        	//neposunuji obraz ale posunuji cele okno do vyssich hodnot (posun doleva) ci do 
		        	//nizsich hodnot (posun vpravo)
		        	//pri dosazeni okraje se hodnota diference tady prictena, odecita - viz dale - 
		        	//aby nedochazelo k posunu mimo okno
		        	_x += dx;
		        	_y += dy;
		        	//nastaveni souradnice X a dale kontrola hranic pro horizontalni pohyb
		       		setX(_x);
		       		//nastaveni souradnice Y a dale kontrola hranic pro vertikalni pohyb
		       		setY(_y);
	        	}
	        	//ulozeni posledni pozice ctene pomoci onTouchListener (tedy pozice prstu)
	       		actualX = previousX/viewState.getZoom();
	       		actualY = previousY/viewState.getZoom();
	       		
	       		
	       		viewState.notifyObservers();
	       		break;
	       	
        	case MotionEvent.ACTION_UP:
        		view.removeCallbacks(longTouchRunnable);
        		zoomControls.setVisibility(View.INVISIBLE);
        		mode = mode.MOVE;
        		break;
        		
        	default:
        		view.removeCallbacks(longTouchRunnable);
        		zoomControls.setVisibility(View.INVISIBLE);
        		mode = mode.MOVE;
        		break;
        }
        		
    return true;
	}
	
	private void setX(float x){
			
		//je v mezich
		if(x<=0 && x>=-(viewWidth-(viewWidth/viewState.getZoom()))){
			viewState.setX(x);
		}
		else{
			if(x>0){
				viewState.setX(0);
				_x -= dx;
			}
			if(x<0){
				viewState.setX(-(viewWidth-(viewWidth/viewState.getZoom())));
				_x -= dx;
			}
		}
	}
	
	private void setY(float y){
		if(y<=0 && y>=-(viewHeight-(viewHeight/viewState.getZoom()))){
			viewState.setY(y);
		}
		else{
			if(y>0){
				viewState.setY(0);
				_y -= dy;
			}
			if(y<0){
				viewState.setY(-(viewHeight-(viewHeight/viewState.getZoom())));
				_y -= dy;
			}
		}
	}
	
	private final Runnable longTouchRunnable = new Runnable() {
		
		public void run() {
			mode = Mode.ZOOM;
			vibrator.vibrate(VIBRATE_TIME);
			zoomControls.setVisibility(View.VISIBLE);
		}
	}; 
	
	public void setZoomControls(ZoomControls zoomControls) {
		this.zoomControls = zoomControls;
		this.zoomControls.setVisibility(View.INVISIBLE);
	}

}
